CloakBrowser 源码级反指纹 Chromium 与 BrowserStation 集成实战

CloakBrowser:源码级反指纹 Chromium 与 BrowserStation 集成实战

市面上大多数「反检测浏览器」的技术路线是注入 JS 或改启动参数,每次 Chrome 升级就断一次,而且注入痕迹本身就是检测信号。CloakBrowser 走的是另一条路——直接改 Chromium C++ 源码,重新编译生成二进制。这篇文章介绍它的原理、用法、与同类方案的差异,以及如何替换 BrowserStation 里的 Chrome 容器,把无头浏览器池升级为反检测池。

一、CloakBrowser 是什么

CloakBrowser 是一个开源的隐身 Chromium 发行版,包含两个东西:

核心主张是「同一套 Playwright API,换三行导入就能绕过大多数反机器人系统」:

# 从这个
from playwright.sync_api import sync_playwright
pw = sync_playwright().start()
browser = pw.chromium.launch()

# 改成这个
from cloakbrowser import launch
browser = launch()

剩下的代码一行不用改。

已通过的检测

官方列出的测试结果(2026 年 4 月):

检测服务 原生 Playwright CloakBrowser
reCAPTCHA v3 分数 0.1(机器人) 0.9(人类)
Cloudflare Turnstile Fail Pass
FingerprintJS 被检测 Pass
BrowserScan 被检测 Normal 4/4
navigator.webdriver true false
TLS 指纹 与 Chrome 不符 完全一致
UA HeadlessChrome Chrome/146.0.0.0

已知局限:DataDome、Kasada、Akamai 这些激进站点仍需要配合住宅代理 + 有头模式 + 字体包才能稳定过。

二、为什么源码级补丁是关键

要理解 CloakBrowser 的价值,先看看同赛道其他方案怎么做。

路线 1:JS 注入(playwright-stealth、puppeteer-extra-stealth)

通过 page.addInitScript() 在页面加载前注入脚本,覆盖 navigator.webdriverwindow.chrome、canvas 等属性。

问题

路线 2:启动参数(undetected-chromedriver)

通过删除 --enable-automation 等启动参数、改启动命令规避检测。

问题

路线 3:Firefox 补丁(Camoufox)

改 Firefox 源码,类似思路但换了引擎。

问题

路线 4:源码级 C++ 补丁(CloakBrowser)

直接改 Chromium 的 C++ 源码,然后编译成二进制:

优势

代价

三、快速上手

安装

Python:

pip install cloakbrowser
# 如果要用代理 IP 自动推断时区和地理位置
pip install cloakbrowser[geoip]

Node.js:

npm install cloakbrowser playwright-core
# 或者用 Puppeteer
npm install cloakbrowser puppeteer-core

首次运行会从 cloakbrowser.dev 自动下载对应平台的 Chromium 二进制(Linux x64/arm64、macOS x86/arm64、Windows x64),缓存在 ~/.cloakbrowser/

最小可运行示例

from cloakbrowser import launch

browser = launch(headless=False)
page = browser.new_page()
page.goto("https://bot.sannysoft.com")
page.screenshot(path="stealth_test.png")
browser.close()

打开截图你会看到 navigator.webdriver 是 false、plugins 有 5 个、chrome 对象存在、UA 是正常 Chrome——全部绿灯。

关键参数

from cloakbrowser import launch

browser = launch(
    proxy="socks5://user:pass@proxy:1080",  # SOCKS5 比 HTTP 代理更稳
    geoip=True,                              # 从代理 IP 推断时区和语言
    headless=False,                          # 有头模式(配合 xvfb 在服务器跑)
    humanize=True,                           # 人类化鼠标/键盘/滚动
    args=["--fingerprint=42069"],            # 固定种子,保持身份一致
)

对于难搞的站点,官方推荐的「黄金组合」:

proxy (住宅) + geoip=True + headless=False + humanize=True

humanize:行为级反检测

单独指纹不够,现代反机器人系统还看鼠标轨迹、键盘间隔、滚动模式。humanize=True 会把所有 Playwright 调用替换为人类化版本:

动作 默认 humanize=True
鼠标移动 瞬移 贝塞尔曲线 + 缓动 + 轻微过冲
点击 立即 真实落点 + 按压时长
键盘 瞬间填充 逐字符计时 + 思考停顿 + 偶尔打错再修正
滚动 跳跃 加速→匀速→减速微步进
browser = launch(humanize=True, human_preset="careful")  # careful 更慢、更像真人

可以进一步自定义:

browser = launch(humanize=True, human_config={
    "mistype_chance": 0.05,       # 5% 打错修正率
    "typing_delay": 100,           # 每字符 100ms
    "idle_between_actions": True,  # 点击间微小鼠标游移
    "idle_between_duration": [0.3, 0.8],
})

持久化 Profile

匿名/隐身模式本身就是检测信号。用 launch_persistent_context() 保留 cookies、localStorage:

from cloakbrowser import launch_persistent_context

ctx = launch_persistent_context("./my-profile", headless=False)
page = ctx.new_page()
page.goto("https://example.com")
# 登录、浏览,cookies 会保留在 ./my-profile/
ctx.close()

# 下次运行自动恢复
ctx = launch_persistent_context("./my-profile")

Docker 与 CDP 服务模式

CloakBrowser 提供官方镜像 cloakhq/cloakbrowser,里面预装了反检测需要的字体包(fonts-noto-color-emoji 等)。

快速验证:

docker run --rm cloakhq/cloakbrowser cloaktest

关键模式:cloakserve——把 CloakBrowser 跑成一个 CDP 服务

docker run -d --name cloak -p 9222:9222 cloakhq/cloakbrowser cloakserve

然后从任何机器用 Playwright 连过去:

from playwright.sync_api import sync_playwright

pw = sync_playwright().start()
browser = pw.chromium.connect_over_cdp("http://localhost:9222")
page = browser.new_page()
page.goto("https://example.com")
browser.close()

这个模式才是把 CloakBrowser 接入到浏览器池系统(比如 BrowserStation)的基础。

每连接不同指纹

cloakserve 支持通过 URL 参数给每个连接分配独立的指纹种子和身份:

b1 = pw.chromium.connect_over_cdp("http://localhost:9222?fingerprint=11111")
b2 = pw.chromium.connect_over_cdp("http://localhost:9222?fingerprint=22222")

# 完整身份参数
b3 = pw.chromium.connect_over_cdp(
    "http://localhost:9222?fingerprint=33333"
    "&timezone=Asia/Tokyo&locale=ja-JP&platform=macos"
    "&hardware-concurrency=4&device-memory=8"
)

相同 seed 复用同一个 Chrome 进程,不同 seed 会起新进程——单容器可以同时承载多个身份。

四、竞品对比

这个赛道大致分三类:代码库级独立浏览器(开源)商业指纹浏览器 SaaS

对比总表

Playwright 原生 playwright-stealth undetected-chromedriver Camoufox CloakBrowser Multilogin/AdsPower 青果/比特
补丁层级 JS 注入 启动参数 C++(Firefox) C++(Chromium) C++(定制 Chromium) C++(定制 Chromium)
reCAPTCHA v3 0.1 0.3-0.5 0.3-0.7 0.7-0.9 0.9 0.7-0.9 0.7-0.9
Cloudflare Turnstile Fail 偶尔过 偶尔过 Pass Pass Pass Pass
抗 Chrome 升级 N/A 经常失效 经常失效
维护活跃度 官方 停滞 停滞 不稳定 活跃 商业公司 商业公司
引擎 Chromium Chromium Chrome Firefox Chromium Chromium Chromium
Playwright 原生支持 ❌(Selenium) 需 CDP 桥接 需 CDP 桥接
License Apache 2.0 MIT MIT MPL MIT + 二进制自由用 商业闭源 商业闭源
价格 免费 免费 免费 免费 免费 $99+/月 ¥200+/月
自托管
团队协作 Profile 管理 Manager 模块(开源)

具体差异

CloakBrowser vs Camoufox

两个最接近的竞品。我仓库里还有一篇 Camofox:给 AI Agent 用的反指纹浏览器 专门讲 Camoufox。

什么时候选 Camoufox:目标站点对 Firefox 不敏感,或者你希望避开 Chromium 独有的检测路径。

CloakBrowser vs playwright-stealth / undetected-chromedriver

前者是二进制方案,后者是配置方案——前者维护成本高(要编译),但效果稳定;后者安装简单,但每次 Chrome 升级都可能断裂,且 JS 注入本身是检测信号。

什么时候还用 playwright-stealth:做一次性的简单抓取,目标站点只有基础检测。

CloakBrowser vs 商业指纹浏览器(Multilogin、AdsPower、BitBrowser)

商业方案的核心价值是多账号管理:图形化 profile 仓库、团队共享、代理绑定、Cookie 同步等。纯做抓取、Agent 自动化用不上这些,CloakBrowser 就够了。

如果你需要账号管理功能,CloakBrowser 也有配套的 Manager 模块 开源版,docker 一行启动:

docker run -p 8080:8080 -v cloakprofiles:/data cloakhq/cloakbrowser-manager

访问 http://localhost:8080 就能可视化管理 profile,是 Multilogin/GoLogin 的免费替代。

选型决策树

是只想跑简单抓取吗?
├─ 是 → playwright-stealth(够用)
└─ 否
   └─ 目标站点有 CF Turnstile / FingerprintJS / reCAPTCHA v3 吗?
      ├─ 没有 → playwright-stealth 或 undetected-chromedriver
      └─ 有
         ├─ 需要多账号 GUI 管理吗?
         │  ├─ 是 + 预算充足 → Multilogin/AdsPower
         │  └─ 是 + 想自托管 → CloakBrowser + Manager 模块
         └─ 否,只做自动化
            ├─ 目标站点对 Firefox 宽容 → Camoufox
            └─ 目标站点偏爱 Chrome 或用 Playwright 生态 → **CloakBrowser**

五、集成到 BrowserStation

之前写过 BrowserStation 浏览器池搭建,默认用的是 zenika/alpine-chrome:100 作为每个 Worker Pod 的 Chrome 容器。这个镜像是普通 Chromium,对上反检测场景就歇菜了。把它替换成 CloakBrowser 后,整个浏览器池立刻变成反检测池。

集成思路

BrowserStation 的架构本来就是 CDP 协议解耦——Head 节点通过 ws://worker:9222/devtools/browser/xxx 代理 WebSocket,只要 Worker 暴露的是标准 CDP 9222,Head 根本不关心底层是哪种 Chrome。

所以集成只需要两步:

  1. chrome 容器从 zenika/alpine-chrome:100 换成 cloakhq/cloakbrowser
  2. 启动命令从 Chromium 的 --remote-debugging-port=9222 换成 cloakserve

修改 RayService YAML

基于之前文章里的 RayService 配置,修改 workerGroupSpecs.template.spec.containers 里的 chrome 容器:

workerGroupSpecs:
- groupName: browser-workers
  replicas: 9
  rayStartParams: {}
  template:
    spec:
      containers:
      - name: ray-worker
        image: browserstation:v1.0
        imagePullPolicy: Never
        resources:
          requests:
            cpu: "100m"
            memory: "256Mi"

      # === 这里改动 ===
      - name: chrome
        image: cloakhq/cloakbrowser:latest   # 替换镜像
        imagePullPolicy: IfNotPresent
        securityContext:
          runAsUser: 0
          runAsNonRoot: false
        command: ["cloakserve"]               # 用 cloakserve 代替默认 chromium
        args:
        - "--host=0.0.0.0"
        - "--port=9222"
        env:
        - name: CLOAKBROWSER_AUTO_UPDATE
          value: "false"                      # 离线环境关闭自动更新
        ports:
        - containerPort: 9222
          name: devtools
        resources:
          requests:
            cpu: "1500m"                      # Cloak 比 alpine-chrome 多吃约 50% CPU
            memory: "1Gi"                     # 含字体、patch 后的 runtime
          limits:
            cpu: "2000m"
            memory: "1536Mi"
        volumeMounts:
        - name: cloak-cache
          mountPath: /root/.cloakbrowser
      volumes:
      - name: cloak-cache
        emptyDir: {}

关键差异

原 alpine-chrome CloakBrowser
镜像大小 ~180MB ~450MB(含字体和 patch 后的 Chromium)
CPU 请求 900m 1500m
内存请求 768Mi 1Gi
启动命令 chromium --remote-debugging-port=... cloakserve --host=... --port=...
二进制存储 镜像内 首次下载 + 挂载缓存目录

离线部署的做法

原文章里用 docker save 导出再 k3s ctr images import 离线导入。CloakBrowser 可以同样操作:

# 拉取 + 预热二进制(避免首次启动下载)
docker run --rm cloakhq/cloakbrowser python -m cloakbrowser install
docker commit $(docker ps -lq) cloakbrowser:preloaded

# 导出
docker save cloakbrowser:preloaded | gzip > cloakbrowser-preloaded.tar.gz

# 分发到所有 Agent 并导入
scp cloakbrowser-preloaded.tar.gz agent01:/opt/k3s-offline/
ssh agent01 "gunzip -c /opt/k3s-offline/cloakbrowser-preloaded.tar.gz | k3s ctr images import -"

然后把 YAML 里的 image: cloakhq/cloakbrowser:latest 改成 image: cloakbrowser:preloaded,同时把 imagePullPolicy 改回 Never

FastAPI 调度层改动

BrowserStation 的 Head 节点负责分配浏览器实例。为了利用 CloakBrowser 的「每连接独立指纹」能力,可以扩展 POST /browsers 接口:

# app/main.py 里的分配接口
@app.post("/browsers")
async def create_browser(req: CreateBrowserRequest):
    worker = pick_least_loaded_worker()

    # 原来的 WS URL
    # ws_url = f"ws://{worker.ip}:9222/devtools/browser/{session_id}"

    # 改成:把用户指定的 fingerprint 参数透传给 cloakserve
    params = urlencode({
        "fingerprint": req.fingerprint_seed or random_seed(),
        "timezone": req.timezone or "",
        "locale": req.locale or "",
        "proxy": req.proxy or "",
        "geoip": "true" if req.geoip else "",
    })
    ws_url = f"ws://{worker.ip}:9222?{params}"

    return {
        "id": session_id,
        "ws_url": ws_url,
        "worker": worker.name,
        "fingerprint": params,
    }

对应的请求体:

class CreateBrowserRequest(BaseModel):
    fingerprint_seed: Optional[int] = None
    timezone: Optional[str] = None
    locale: Optional[str] = None
    proxy: Optional[str] = None
    geoip: bool = False
    humanize: bool = True

这样客户端可以明确指定身份:

import httpx

r = httpx.post("http://10.0.0.10:30050/browsers", json={
    "fingerprint_seed": 42069,
    "timezone": "Asia/Shanghai",
    "locale": "zh-CN",
    "proxy": "socks5://user:pass@sg-proxy:1080",
    "geoip": True,
})
ws_url = r.json()["ws_url"]

客户端对接

客户端代码几乎不用变(Playwright CDP 连接就能拿到反检测效果):

from playwright.async_api import async_playwright
import httpx, asyncio

API = "http://10.0.0.10:30050"

async def main():
    r = httpx.post(f"{API}/browsers", json={
        "fingerprint_seed": 12345,
        "timezone": "America/New_York",
        "locale": "en-US",
        "humanize": True,
    })
    info = r.json()

    async with async_playwright() as p:
        browser = await p.chromium.connect_over_cdp(info["ws_url"])
        page = browser.contexts[0].pages[0]
        await page.goto("https://bot.sannysoft.com")
        await page.screenshot(path="cloak-via-browserstation.png")
        await browser.close()

    httpx.delete(f"{API}/browsers/{info['id']}")

asyncio.run(main())

注意:humanize 是 wrapper 层特性,跨 CDP 连接时默认丢失。要在客户端恢复,需要手动 patch:

// Node 客户端
import { patchBrowser, resolveConfig } from 'cloakbrowser/human';

const browser = await playwright.chromium.connectOverCDP(wsUrl);
patchBrowser(browser, resolveConfig('default'));

Python 目前还需要在客户端自己实现人类化(或者把 humanize 逻辑搬到 BrowserStation 的 FastAPI 层做一次封装)。

资源规划调整

原配置是每个 Worker Pod 跑 1 个 Chrome,基于新资源需求重新算账:

资源 原配置(alpine-chrome) CloakBrowser
单 Worker CPU 1 core (100m + 900m) 1.6 core (100m + 1500m)
单 Worker 内存 1Gi (256Mi + 768Mi) 1.25Gi (256Mi + 1Gi)
镜像大小 ~200MB ~450MB
每台 8C16G Agent 能跑几个 Worker 7-8 4-5

10 台 Agent,单 Worker 配 CloakBrowser 的话,集群容量大概从 72 个并发浏览器降到 45 个并发浏览器。如果并发需求高,要么扩节点,要么把 Agent 规格提到 16C32G。

另一种策略是用 cloakserve多连接单进程特性:同一个容器里跑多个浏览器会话(不同 fingerprint seed 走不同 Chrome 进程,但共享同一个 cloakserve 服务),可以把 Worker Pod 改成一对多:

- name: chrome
  image: cloakhq/cloakbrowser:latest
  command: ["cloakserve"]
  args:
  - "--host=0.0.0.0"
  - "--port=9222"
  - "--max-browsers=5"    # 假设支持,依具体版本
  resources:
    requests:
      cpu: "4000m"         # 预留足够 CPU
      memory: "4Gi"

然后在 Ray Actor 层做二次池化,一个容器对外呈现 5 个 "virtual browser",这样整体密度接近原来。

一条完整的验证流程

部署好之后,跑一遍端到端:

# 1. 确认 Pod 跑起来
kubectl get pods -n ray-system
# browser-cluster-worker-xxx    2/2   Running   ← 注意 2/2 sidecar

# 2. 确认 cloakserve 就绪
kubectl exec -n ray-system browser-cluster-worker-xxx -c chrome -- \
  curl -s http://localhost:9222/json/version | jq
# {
#   "Browser": "Chrome/146.0.7680.177",        ← 注意不是 HeadlessChrome
#   "Protocol-Version": "1.3",
#   "User-Agent": "Mozilla/5.0 ... Chrome/146.0.0.0 ..."
# }

# 3. 从 Head 申请一个实例
curl -X POST http://10.0.0.10:30050/browsers \
  -H "Content-Type: application/json" \
  -d '{"fingerprint_seed": 42069, "humanize": true}'

# 4. 跑一次 bot.sannysoft 测试
python cloak-via-browserstation-test.py

通过 bot.sannysoft.com 看到全部 webdriver / plugins / chrome / permission 绿灯,就成了。

六、几个实战注意点

  1. 字体是关键:Kasada/Akamai 会渲染 emoji 到 canvas 然后算 hash。CloakBrowser 官方镜像已经装了 emoji 字体,但如果你自己基于 python:slim 构建,要手动 apt install fonts-noto-color-emoji fonts-freefont-ttf

  2. page.wait_for_timeout() 会降分:这个 Playwright API 会发 CDP 协议包,reCAPTCHA 能检测到。换成 time.sleep()await asyncio.sleep()

  3. 住宅代理比浏览器指纹更重要:数据中心 IP 无论指纹多完美都会被 IP 信誉系统拦。预算够就买住宅代理,预算紧就买共享住宅池(如 Bright Data、NetNut 的 pay-as-you-go 套餐)。

  4. 固定 seed vs 随机 seed

    • 同一站点反复访问 → 固定 seed(像回头客)
    • 多账号独立隔离 → 每账号一个固定 seed
    • 一次性大规模抓取不同域名 → 随机 seed
  5. Playwright 优于 Puppeteer:官方明确建议在处理 reCAPTCHA Enterprise 时用 Playwright,Puppeteer 的 CDP 协议会泄露更多自动化信号。

  6. Binary License 限制商用行为:wrapper 代码 MIT,但编译后的 Chromium 二进制是「自由使用但禁止重分发」。做内部自动化没问题,但不能打包进自己的商业产品分发。

七、什么时候不该用 CloakBrowser

为了平衡,列几个不该选它的场景:

总结

CloakBrowser 在反检测赛道做对了两件事:把补丁打在最难被检测的 C++ 层,以及完全兼容 Playwright 生态。对比之前要么用破碎的 JS 注入、要么花钱买商业指纹浏览器的局面,它给了一个开源、免费、能自托管、且足够生产用的选项。

接入到 BrowserStation 这类浏览器池系统里,只需要替换 chrome sidecar 的镜像和启动命令,上层调度逻辑一点不用改——这是 CDP 协议解耦架构的最大好处。做出来之后,你就有了一个 10 节点、45 并发、分布式、反检测的浏览器池,可以撑 AI Agent 批量跑真实网页任务,也可以给爬虫团队当共享基础设施。

参考资源: